﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="RendererImage.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010, 2011
//       Roland Rosenkranz (Glatzemann@email.de)
//
//    Based on libnoise by Jason Bevins
//      Copyright (C) 2003, 2004 Jason Bevins (licensed under LGPL)
// </copyright>
// <license>
//   This file is part of starLiGHT.Noise.
//
//   starLiGHT.Noise is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Noise is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Noise. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Noise are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 10385                 $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-06-14 07:59:17 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.Noise.Noise
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;

    #endregion

    /// Renders an this.image from a noise map.
    ///
    /// This class renders an this.image given the contents of a noise-map object.
    ///
    /// An application can configure the output of the this.image in three ways:
    /// - Specify the color gradient.
    /// - Specify the light source parameters.
    /// - Specify the background this.image.
    ///
    /// <b>Specify the color gradient</b>
    ///
    /// This class uses a color gradient to calculate the color for each pixel
    /// in the destination this.image according to the value from the corresponding
    /// position in the noise map.
    ///
    /// A color gradient is a list of gradually-changing colors.  A color
    /// gradient is defined by a list of <i>gradient points</i>.  Each
    /// gradient point has a position and a color.  In a color gradient, the
    /// colors between two adjacent gradient points are linearly interpolated.
    ///
    /// For example, suppose this class contains the following color gradient:
    ///
    /// - -1.0 maps to dark blue.
    /// - -0.2 maps to light blue.
    /// - -0.1 maps to tan.
    /// - 0.0 maps to green.
    /// - 1.0 maps to white.
    ///
    /// The value 0.5 maps to a greenish-white color because 0.5 is halfway
    /// between 0.0 (mapped to green) and 1.0 (mapped to white).
    ///
    /// The value -0.6 maps to a medium blue color because -0.6 is halfway
    /// between -1.0 (mapped to dark blue) and -0.2 (mapped to light blue).
    ///
    /// The color gradient requires a minimum of two gradient points.
    ///
    /// This class contains two pre-made gradients: a grayscale gradient and a
    /// color gradient suitable for terrain.  To use these pre-made gradients,
    /// call the BuildGrayscaleGradient() or BuildTerrainGradient() methods,
    /// respectively.
    ///
    /// @note The color value passed to AddGradientPoint() has an alpha
    /// channel.  This alpha channel specifies how a pixel in the background
    /// this.image (if specified) is blended with the calculated color.  If the
    /// alpha value is high, this class weighs the blend towards the
    /// calculated color, and if the alpha value is low, this class weighs the
    /// blend towards the color from the corresponding pixel in the background
    /// this.image.
    ///
    /// <b>Specify the light source parameters</b>
    ///
    /// This class contains a parallel light source that lights the this.image.  It
    /// interprets the noise map as a bump map.
    ///
    /// To enable or disable lighting, pass a bool value to the
    /// EnableLight() method.
    ///
    /// To set the position of the light source in the "sky", call the
    /// SetLightAzimuth() and SetLightElev() methods.
    ///
    /// To set the color of the light source, call the SetLightColor() method.
    ///
    /// To set the intensity of the light source, call the SetLightIntensity()
    /// method.  A good intensity value is 2.0, although that value tends to
    /// "wash out" very light colors from the this.image.
    /// 
    /// To set the contrast amount between areas in light and areas in shadow,
    /// call the SetLightContrast() method.  Determining the correct contrast
    /// amount requires some trial and error, but if your application
    /// interprets the noise map as a height map that has its elevation values
    /// measured in meters and has a horizontal resolution of @a h meters, a
    /// good contrast amount to use is (1.0 / @a h).
    /// 
    /// <b>Specify the background this.image</b>
    ///
    /// To specify a background this.image, pass an Image object to the
    /// SetBackgroundImage() method.
    ///
    /// This class determines the color of a pixel in the destination this.image by
    /// blending the calculated color with the color of the corresponding
    /// pixel from the background this.image.
    ///
    /// The blend amount is determined by the alpha of the calculated color.
    /// If the alpha value is high, this class weighs the blend towards the
    /// calculated color, and if the alpha value is low, this class weighs the
    /// blend towards the color from the corresponding pixel in the background
    /// this.image.
    ///
    /// <b>Rendering the this.image</b>
    ///
    /// To render the this.image, perform the following steps:
    /// - Pass a NoiseMap object to the SetSourceNoiseMap() method.
    /// - Pass an Image object to the SetDestImage() method.
    /// - Pass an Image object to the SetBackgroundImage() method (optional)
    /// - Call the Render() method.
    public class RendererImage
    {
        /// HACK:
        public Color[,] image = new Color[256, 256];

        /// Pi.
        private const double PI = 3.1415926535897932385;

        /// Square root of 2.
        private const double SQRT_2 = 1.4142135623730950488;

        /// Square root of 3.
        private const double SQRT_3 = 1.7320508075688772935;

        /// Converts an angle from degrees to radians.
        private const double DEG_TO_RAD = PI / 180.0;

        /// Converts an angle from radians to degrees.
        private const double RAD_TO_DEG = 1.0 / DEG_TO_RAD;

        /// The cosine of the azimuth of the light source.
        private double cosAzimuth;

        /// The cosine of the elevation of the light source.
        private double cosElev;

        /// The color gradient used to specify the this.image colors.
        private GradientColor gradient;

        /// A flag specifying whether lighting is enabled.
        private bool isLightEnabled;

        /// A flag specifying whether wrapping is enabled.
        private bool isWrapEnabled;

        /// The azimuth of the light source, in degrees.
        private double lightAzimuth;

        /// The brightness of the light source.
        private double lightBrightness;

        /// The color of the light source.
        private Color lightColor;

        /// The contrast between areas in light and areas in shadow.
        private double lightContrast;

        /// The elevation of the light source, in degrees.
        private double lightElev;

        /// The intensity of the light source.
        private double lightIntensity;

        /// A pointer to the background this.image.
        private Texture2D backgroundImage;

        /// A pointer to the destination this.image.
        private Texture2D destImage;

        /// A pointer to the source noise map.
        private NoiseMap sourceNoiseMap;

        /// Used by the CalcLightIntensity() method to recalculate the light
        /// values only if the light parameters change.
        ///
        /// When the light parameters change, this value is set to True.  When
        /// the CalcLightIntensity() method is called, this value is set to
        /// false.
        private bool recalcLightValues;

        /// The sine of the azimuth of the light source.
        private double sinAzimuth;

        /// The sine of the elevation of the light source.
        private double sinElev;

        /// Constructor.
        public RendererImage()
        {
            this.isLightEnabled = false;
            this.isWrapEnabled = false;
            this.lightAzimuth = 45.0;
            this.lightBrightness = 1.0;
            this.lightColor = new Color(255, 255, 255, 255);
            this.lightContrast = 1.0;
            this.lightElev = 45.0;
            this.lightIntensity = 1.0;
            this.backgroundImage = null;
            this.destImage = null;
            this.sourceNoiseMap = null;
            this.recalcLightValues = true;
            this.BuildGrayscaleGradient();
        }

        /// Adds a gradient point to this gradient object.
        ///
        /// @param gradientPos The position of this gradient point.
        /// @param gradientColor The color of this gradient point.
        ///
        /// @pre No two gradient points have the same position.
        ///
        /// @throw noise::ExceptionInvalidParam See the preconditions.
        ///
        /// This object uses a color gradient to calculate the color for each
        /// pixel in the destination this.image according to the value from the
        /// corresponding position in the noise map.
        ///
        /// The gradient requires a minimum of two gradient points.
        ///
        /// The specified color value passed to this method has an alpha
        /// channel.  This alpha channel specifies how a pixel in the
        /// background this.image (if specified) is blended with the calculated
        /// color.  If the alpha value is high, this object weighs the blend
        /// towards the calculated color, and if the alpha value is low, this
        /// object weighs the blend towards the color from the corresponding
        /// pixel in the background this.image.
        public void AddGradientPoint(double gradientPos, Color gradientColor)
        {
            this.gradient.AddGradientPoint(gradientPos, gradientColor);
        }

        /// Builds a grayscale gradient.
        ///
        /// @post The original gradient is cleared and a grayscale gradient is
        /// created.
        ///
        /// This color gradient contains the following gradient points:
        /// - -1.0 maps to black
        /// - 1.0 maps to white
        public void BuildGrayscaleGradient()
        {
            this.gradient = new GradientColor();

            this.ClearGradient();
            this.gradient.AddGradientPoint(-1.0, new Color(0, 0, 0, 255));
            this.gradient.AddGradientPoint(1.0, new Color(255, 255, 255, 255));
        }

        /// Builds a color gradient suitable for terrain.
        ///
        /// @post The original gradient is cleared and a terrain gradient is
        /// created.
        ///
        /// This gradient color at position 0.0 is the "sea level".  Above
        /// that value, the gradient contains greens, browns, and whites.
        /// Below that value, the gradient contains various shades of blue.
        public void BuildTerrainGradient()
        {
        }

        /// Clears the color gradient.
        ///
        /// Before calling the Render() method, the application must specify a
        /// new color gradient with at least two gradient points.
        public void ClearGradient()
        {
            if (this.gradient != null) this.gradient.Clear();
        }

        /// Enables or disables the light source.
        ///
        /// @param enable A flag that enables or disables the light source.
        ///
        /// If the light source is enabled, this object will interpret the
        /// noise map as a bump map.
        public void EnableLight(bool enable)
        {
            this.isLightEnabled = enable;
        }

        public void EnableLight()
        {
            this.isLightEnabled = true;
        }

        /// Enables or disables noise-map wrapping.
        ///
        /// @param enable A flag that enables or disables noise-map wrapping.
        ///
        /// This object requires five points (the initial point and its four
        /// neighbors) to calculate light shading.  If wrapping is enabled,
        /// and the initial point is on the edge of the noise map, the
        /// appropriate neighbors that lie outside of the noise map will
        /// "wrap" to the opposite side(s) of the noise map.  Otherwise, the
        /// appropriate neighbors are cropped to the edge of the noise map.
        ///
        /// Enabling wrapping is useful when creating spherical renderings and
        /// tileable textures.
        public void EnableWrap(bool enable)
        {
            this.isWrapEnabled = enable;
        }

        public void EnableWrap()
        {
            this.isWrapEnabled = true;
        }

        /// Returns the azimuth of the light source, in degrees.
        ///
        /// @returns The azimuth of the light source.
        ///
        /// The azimuth is the location of the light source around the
        /// horizon:
        /// - 0.0 degrees is east.
        /// - 90.0 degrees is north.
        /// - 180.0 degrees is west.
        /// - 270.0 degrees is south.
        public double GetLightAzimuth()
        {
            return this.lightAzimuth;
        }

        /// Returns the brightness of the light source.
        ///
        /// @returns The brightness of the light source.
        public double GetLightBrightness()
        {
            return this.lightBrightness;
        }

        /// Returns the color of the light source.
        ///
        /// @returns The color of the light source.
        public Color GetLightColor()
        {
            return this.lightColor;
        }

        /// Returns the contrast of the light source.
        ///
        /// @returns The contrast of the light source.
        ///
        /// The contrast specifies how sharp the boundary is between the
        /// light-facing areas and the shadowed areas.
        ///
        /// The contrast determines the difference between areas in light and
        /// areas in shadow.  Determining the correct contrast amount requires
        /// some trial and error, but if your application interprets the noise
        /// map as a height map that has a spatial resolution of @a h meters
        /// and an elevation resolution of 1 meter, a good contrast amount to
        /// use is (1.0 / @a h).
        public double GetLightContrast()
        {
            return this.lightContrast;
        }

        /// Returns the elevation of the light source, in degrees.
        ///
        /// @returns The elevation of the light source.
        ///
        /// The elevation is the angle above the horizon:
        /// - 0 degrees is on the horizon.
        /// - 90 degrees is straight up.
        public double GetLightElev()
        {
            return this.lightElev;
        }

        /// Returns the intensity of the light source.
        ///
        /// @returns The intensity of the light source.
        public double GetLightIntensity()
        {
            return this.lightIntensity;
        }

        /// Determines if the light source is enabled.
        ///
        /// @returns
        /// - @a true if the light source is enabled.
        /// - @a false if the light source is disabled.
        public bool IsLightEnabled()
        {
            return this.isLightEnabled;
        }

        /// Determines if noise-map wrapping is enabled.
        ///
        /// @returns
        /// - @a true if noise-map wrapping is enabled.
        /// - @a false if noise-map wrapping is disabled.
        ///
        /// This object requires five points (the initial point and its four
        /// neighbors) to calculate light shading.  If wrapping is enabled,
        /// and the initial point is on the edge of the noise map, the
        /// appropriate neighbors that lie outside of the noise map will
        /// "wrap" to the opposite side(s) of the noise map.  Otherwise, the
        /// appropriate neighbors are cropped to the edge of the noise map.
        ///
        /// Enabling wrapping is useful when creating spherical renderings and
        /// tileable textures
        public bool IsWrapEnabled()
        {
            return this.isWrapEnabled;
        }

        /// Renders the destination this.image using the contents of the source
        /// noise map and an optional background this.image.
        ///
        /// @pre SetSourceNoiseMap() has been previously called.
        /// @pre SetDestImage() has been previously called.
        /// @pre There are at least two gradient points in the color gradient.
        /// @pre No two gradient points have the same position.
        /// @pre If a background this.image was specified, it has the exact same
        /// size as the source height map.
        ///
        /// @post The original contents of the destination this.image is destroyed.
        ///
        /// @throw noise::ExceptionInvalidParam See the preconditions.
        ///
        /// The background this.image and the destination this.image can safely refer to
        /// the same this.image, although in this case, the destination this.image is
        /// irretrievably blended into the background this.image.
        public void Render()
        {
            if (this.sourceNoiseMap == null
////////TODO:                 || this.pDestImage == null
                 || this.sourceNoiseMap.GetWidth() <= 0
                 || this.sourceNoiseMap.GetHeight() <= 0
                 || this.gradient.GetGradientPointCount() < 2)
            {
                ////throw new Exception("invalid param");
                return;
            }

            int width = this.sourceNoiseMap.GetWidth();
            int height = this.sourceNoiseMap.GetHeight();

            // If a background this.image was provided, make sure it is the same size the
            // source noise map.
            if (this.backgroundImage != null)
            {
                if (this.backgroundImage.Width != width || this.backgroundImage.Height != height)
                    throw new Exception("invalid param");
            }

            // Create the destination this.image.  It is safe to reuse it if this is also the
            // background this.image.
            ////if (this.pDestImage != this.pBackgroundImage)
            ////    this.pDestImage = new Bitmap(width, height);

            for (int y = 0; y < height; y++)
            {
                Color background = new Color();
                if (this.backgroundImage != null)
                {
                    ////pBackground =  GetPixel((Texture2D)this.pBackgroundImage, 0, y);
                    background = this.GetPixel(0, y);
                }
                ////float pSource = this.pSourceNoiseMap.GetConstSlabPtr(y);
                for (int x = 0; x < width; x++)
                {
                    Color dest; // = ((Bitmap)this.pDestImage).GetPixel(x, y);
                    float source = this.sourceNoiseMap.GetConstSlabPtr(x, y);
                    // Get the color based on the value at the current point in the noise
                    // map.
                    Color destColor = this.gradient.GetColor(source);

                    // If lighting is enabled, calculate the light intensity based on the
                    // rate of change at the current point in the noise map.
                    double lightIntensity;
                    if (this.isLightEnabled)
                    {
                        // Calculate the positions of the current point's four-neighbors.
                        int leftOffsetX, rightOffsetX;
                        int upwardsOffsetY, downOffsetY;
                        if (this.isWrapEnabled)
                        {
                            if (x == 0)
                            {
                                leftOffsetX = (int)width - 1;
                                rightOffsetX = 1;
                            }
                            else if (x == (int)width - 1)
                            {
                                leftOffsetX = -1;
                                rightOffsetX = -((int)width - 1);
                            }
                            else
                            {
                                leftOffsetX = -1;
                                rightOffsetX = 1;
                            }
                            if (y == 0)
                            {
                                downOffsetY = (int)height - 1;
                                upwardsOffsetY = 1;
                            }
                            else if (y == (int)height - 1)
                            {
                                downOffsetY = -1;
                                upwardsOffsetY = -((int)height - 1);
                            }
                            else
                            {
                                downOffsetY = -1;
                                upwardsOffsetY = 1;
                            }
                        }
                        else
                        {
                            if (x == 0)
                            {
                                leftOffsetX = 0;
                                rightOffsetX = 1;
                            }
                            else if (x == (int)width - 1)
                            {
                                leftOffsetX = -1;
                                rightOffsetX = 0;
                            }
                            else
                            {
                                leftOffsetX = -1;
                                rightOffsetX = 1;
                            }
                            if (y == 0)
                            {
                                downOffsetY = 0;
                                upwardsOffsetY = 1;
                            }
                            else if (y == (int)height - 1)
                            {
                                downOffsetY = -1;
                                upwardsOffsetY = 0;
                            }
                            else
                            {
                                downOffsetY = -1;
                                upwardsOffsetY = 1;
                            }
                        }
                        ////yDownOffset *= this.pSourceNoiseMap.GetStride();
                        ////yUpOffset   *= this.pSourceNoiseMap.GetStride();

                        // Get the noise value of the current point in the source noise map
                        // and the noise values of its four-neighbors.
                        double nc = (double)this.sourceNoiseMap.GetConstSlabPtr(x, y);
                        double nl = (double)this.sourceNoiseMap.GetConstSlabPtr(x + leftOffsetX, y);
                        double nr = (double)this.sourceNoiseMap.GetConstSlabPtr(x + rightOffsetX, y);
                        double nd = (double)this.sourceNoiseMap.GetConstSlabPtr(x, y + downOffsetY);
                        double nu = (double)this.sourceNoiseMap.GetConstSlabPtr(x, y + upwardsOffsetY);

                        // Now we can calculate the lighting intensity.
                        lightIntensity = this.CalcLightIntensity(nc, nl, nr, nd, nu);
                        lightIntensity *= this.lightBrightness;

                    }
                    else
                    {

                        // These values will apply no lighting to the destination this.image.
                        lightIntensity = 1.0;
                    }

                    // Get the current background color from the background this.image.
                    Color backgroundColor = new Color(0, 0, 0, 0);
                    if (this.backgroundImage != null)
                    {
                        ////backgroundColor = GetPixel((Texture2D)this.pBackgroundImage, x, y);
                        backgroundColor = this.GetPixel(x, y);
                    }

                    // Blend the destination color, background color, and the light
                    // intensity together, then update the destination this.image with that
                    // color.
                    dest = this.CalcDestColor(destColor, backgroundColor, lightIntensity);
                    ////((Bitmap)this.pDestImage).SetPixel(x, y, pDest); //Color.FromArgb(128,pDest.R, pDest.G, pDest.B));
                    ////SetPixel((Texture2D)this.pDestImage, x, y, pDest);
                    this.SetPixel(x, y, dest);
                }
            }
        }

        ////private void SetPixel(Texture2D tex, int x, int y, Color pDest)
        ////{
        ////    Rectangle r = new Rectangle(x, y, 1, 1);
        ////    Color[] color = new Color[1];
        ////    color[0] = pDest;

        ////    tex.SetData<Color>(0, r, color, 0, 1, SetDataOptions.None);
        ////}

        ////private Color GetPixel(Texture2D tex, int x, int y)
        ////{
        ////    Color[] color = new Color[1];
        ////    Rectangle rect = new Rectangle(x, y, 1, 1);
        ////    tex.GetData<Color>(0, rect, color, 0, 1);
        ////    return color[0];
        ////}

        /// Sets the background this.image.
        ///
        /// @param backgroundImage The background this.image.
        ///
        /// If a background this.image has been specified, the Render() method
        /// blends the pixels from the background this.image onto the corresponding
        /// pixels in the destination this.image.  The blending weights are
        /// determined by the alpha channel in the pixels in the destination
        /// this.image.
        ///
        /// The destination this.image must exist throughout the lifetime of this
        /// object unless another this.image replaces that this.image.
        public void SetBackgroundImage(Texture2D backgroundImage)
        {
            this.backgroundImage = backgroundImage;
        }

        /// Sets the destination this.image.
        ///
        /// @param destImage The destination this.image.
        ///
        /// The destination this.image will contain the rendered this.image after a
        /// successful call to the Render() method.
        ///
        /// The destination this.image must exist throughout the lifetime of this
        /// object unless another this.image replaces that this.image.
        public void SetDestImage(Texture2D destImage)
        {
            this.destImage = destImage;
        }

        /// Sets the azimuth of the light source, in degrees.
        ///
        /// @param lightAzimuth The azimuth of the light source.
        ///
        /// The azimuth is the location of the light source around the
        /// horizon:
        /// - 0.0 degrees is east.
        /// - 90.0 degrees is north.
        /// - 180.0 degrees is west.
        /// - 270.0 degrees is south.
        ///
        /// Make sure the light source is enabled via a call to the
        /// EnableLight() method before calling the Render() method.
        public void SetLightAzimuth(double lightAzimuth)
        {
            this.lightAzimuth = lightAzimuth;
            this.recalcLightValues = true;
        }

        /// Sets the brightness of the light source.
        ///
        /// @param lightBrightness The brightness of the light source.
        ///
        /// Make sure the light source is enabled via a call to the
        /// EnableLight() method before calling the Render() method.
        public void SetLightBrightness(double lightBrightness)
        {
            this.lightBrightness = lightBrightness;
            this.recalcLightValues = true;
        }

        /// Sets the color of the light source.
        ///
        /// @param lightColor The light color.
        ///
        /// Make sure the light source is enabled via a call to the
        /// EnableLight() method before calling the Render() method.
        public void SetLightColor(Color lightColor)
        {
            this.lightColor = lightColor;
        }

        /// Sets the contrast of the light source.
        ///
        /// @param lightContrast The contrast of the light source.
        ///
        /// @pre The specified light contrast is positive.
        ///
        /// @throw noise::ExceptionInvalidParam See the preconditions.
        ///
        /// The contrast specifies how sharp the boundary is between the
        /// light-facing areas and the shadowed areas.
        ///
        /// The contrast determines the difference between areas in light and
        /// areas in shadow.  Determining the correct contrast amount requires
        /// some trial and error, but if your application interprets the noise
        /// map as a height map that has a spatial resolution of @a h meters
        /// and an elevation resolution of 1 meter, a good contrast amount to
        /// use is (1.0 / @a h).
        ///
        /// Make sure the light source is enabled via a call to the
        /// EnableLight() method before calling the Render() method.
        public void SetLightContrast(double lightContrast)
        {
            if (lightContrast <= 0.0)
            {
                throw new Exception("invalid param");
            }

            this.lightContrast = lightContrast;
            this.recalcLightValues = true;
        }

        /// Sets the elevation of the light source, in degrees.
        ///
        /// @param lightElev The elevation of the light source.
        ///
        /// The elevation is the angle above the horizon:
        /// - 0 degrees is on the horizon.
        /// - 90 degrees is straight up.
        ///
        /// Make sure the light source is enabled via a call to the
        /// EnableLight() method before calling the Render() method.
        public void SetLightElev(double lightElev)
        {
            this.lightElev = lightElev;
            this.recalcLightValues = true;
        }

        /// Returns the intensity of the light source.
        ///
        /// @returns The intensity of the light source.
        ///
        /// A good value for intensity is 2.0.
        ///
        /// Make sure the light source is enabled via a call to the
        /// EnableLight() method before calling the Render() method.
        public void SetLightIntensity(double lightIntensity)
        {
            if (lightIntensity < 0.0)
            {
                throw new Exception("invalid param");
            }

            this.lightIntensity = lightIntensity;
            this.recalcLightValues = true;
        }

        /// Sets the source noise map.
        ///
        /// @param sourceNoiseMap The source noise map.
        ///
        /// The destination this.image must exist throughout the lifetime of this
        /// object unless another this.image replaces that this.image.
        public void SetSourceNoiseMap(NoiseMap sourceNoiseMap)
        {
            this.sourceNoiseMap = sourceNoiseMap;
        }

        /// Calculates the destination color.
        ///
        /// @param sourceColor The source color generated from the color
        /// gradient.
        /// @param backgroundColor The color from the background this.image at the
        /// corresponding position.
        /// @param lightValue The intensity of the light at that position.
        ///
        /// @returns The destination color.
        private Color CalcDestColor(Color sourceColor, Color backgroundColor, double lightValue)
        {
            double sourceRed = (double)sourceColor.R / 255.0;
            double sourceGreen = (double)sourceColor.G / 255.0;
            double sourceBlue = (double)sourceColor.B / 255.0;
            double sourceAlpha = (double)sourceColor.A / 255.0;
            double backgroundRed = (double)backgroundColor.R / 255.0;
            double backgroundGreen = (double)backgroundColor.G / 255.0;
            double backgroundBlue = (double)backgroundColor.B / 255.0;

            // First, blend the source color to the background color using the alpha
            // of the source color.
            double red = interp.LinearInterp(backgroundRed, sourceRed, sourceAlpha);
            double green = interp.LinearInterp(backgroundGreen, sourceGreen, sourceAlpha);
            double blue = interp.LinearInterp(backgroundBlue, sourceBlue, sourceAlpha);

            if (this.isLightEnabled)
            {
                // Now calculate the light color.
                double lightRed = lightValue * (double)this.lightColor.R / 255.0;
                double lightGreen = lightValue * (double)this.lightColor.G / 255.0;
                double lightBlue = lightValue * (double)this.lightColor.B / 255.0;

                // Apply the light color to the new color.
                red *= lightRed;
                green *= lightGreen;
                blue *= lightBlue;
            }

            // Clamp the color channels to the (0..1) range.
            red = (red < 0.0) ? 0.0 : red;
            red = (red > 1.0) ? 1.0 : red;
            green = (green < 0.0) ? 0.0 : green;
            green = (green > 1.0) ? 1.0 : green;
            blue = (blue < 0.0) ? 0.0 : blue;
            blue = (blue > 1.0) ? 1.0 : blue;

            // Rescale the color channels to the noise::uint8 (0..255) range and return
            // the new color.
            ////Color newColor = new Color((int)((int)(red * 255.0) & 0xff), (int)((int)(green * 255.0) & 0xff), (int)((int)(blue * 255.0) & 0xff), (int)Math.Max(sourceColor.A, backgroundColor.A));

            Color newColor = new Color(
                                       (byte)(red * 255), 
                                       (byte)(green * 255),
                                       (byte)(blue * 255),
                                       Math.Max(sourceColor.A, backgroundColor.A));

            return newColor;
        }

        /// Calculates the intensity of the light given some elevation values.
        ///
        /// @param center Elevation of the center point.
        /// @param left Elevation of the point directly left of the center
        /// point.
        /// @param right Elevation of the point directly right of the center
        /// point.
        /// @param down Elevation of the point directly below the center
        /// point.
        /// @param up Elevation of the point directly above the center point.
        ///
        /// These values come directly from the noise map.
        private double CalcLightIntensity(double center, double left, double right, double down, double up)
        {
            // Recalculate the sine and cosine of the various light values if
            // necessary so it does not have to be calculated each time this method is
            // called.
            if (this.recalcLightValues)
            {
                this.cosAzimuth = Math.Cos(this.lightAzimuth * DEG_TO_RAD);
                this.sinAzimuth = Math.Sin(this.lightAzimuth * DEG_TO_RAD);
                this.cosElev = Math.Cos(this.lightElev * DEG_TO_RAD);
                this.sinElev = Math.Sin(this.lightElev * DEG_TO_RAD);
                this.recalcLightValues = false;
            }

            // Now do the lighting calculations.
            const double I_MAX = 1.0;
            double io = I_MAX * SQRT_2 * this.sinElev / 2.0;
            double ix = (I_MAX - io) * this.lightContrast * SQRT_2 * this.cosElev * this.cosAzimuth;
            double iy = (I_MAX - io) * this.lightContrast * SQRT_2 * this.cosElev * this.sinAzimuth;
            double intensity = ix * (left - right) + iy * (down - up) + io;
            if (intensity < 0.0)
            {
                intensity = 0.0;
            }
            return intensity;
        }

        private void SetPixel(int x, int y, Color dest)
        {
            this.image[x, y] = dest;
        }

        private Color GetPixel(int x, int y)
        {
            return this.image[x, y];
        }
    }
}

